﻿/**
 * 滚动条动的时候的效果
 * comboBox.js TODO 分页 ? TODO 多选 ? TODO 拼音首写匹配中文 ?
 */
define(function(require, exports, module) {
	var $ = jQuery = require('jquery');
	var noSelect = require('noSelect');
	var browser = require("browser")();
	var Position = require('position');
	require('comboBox/comboBox.css');
	var KEY = {
		UP : 38,
		DOWN : 40,
		DEL : 46,
		TAB : 9,
		RETURN : 13,
		ESC : 27,
		COMMA : 188,
		PAGEUP : 33,
		PAGEDOWN : 34,
		BACKSPACE : 8
	};

	function ComboBox($elem, options) {
		var defaultOptions = {
			dataArray : [],
			url : "",
			params : {},
			selectOnly : false,
			refEl : '',
			pageSize : 10
		};
		options = jQuery.extend({}, defaultOptions, options);
		if (!$elem || !($elem instanceof jQuery) || $elem.length !== 1 || $elem.get(0).tagName.toUpperCase() !== 'INPUT') {
			throw new Error('Invalid parameter for jquery.combo, jQuery object with one element with INPUT tag expected.');
		}
		this.options = options;
		this.el = $elem;
		var self = this;
		// 改变input的形态--添加下拉按钮，修改样式
		if (options.selectOnly) {
			$elem.attr("readonly", "true");
		}
		// alert($elem.css("heigth"));
		// $elem.css("width", (Number($elem.css("width").slice(0,-2))- 15 ) +
		// "px");

		$elem.wrap($("<span/>").addClass("combobox-span"));
		$elem.after($("<div>").addClass("combobox-menu"));
		var hid = $("<input>").attr({
			type : "hidden"
		}).val($elem.val());
		var ele = $elem.get(0);
		if (ele.name != '') {
			hid.attr("name", ele.name + "_hidden");
		}
		if (ele.id != '') {
			hid.attr("id", ele.id + "_hidden");
		}
		$elem.after(hid);
		$elem.after($("<span>").addClass("combobox-button").html("&nbsp;").bind("click", options, function() {
			$elem.focus();
			self.requestData(false);
		}));

		$elem.focusin(function() {
			$(this).parent().css({
				'border-color' : '#3892D3'
			});
		});

		$elem.focusout(function() {
			$(this).parent().css({
				'border-color' : 'silver #d9d9d9 #d9d9d9'
			});
		});

		$elem.addClass("combobox-input");
		$elem.bind("click", options, function() {
			self.requestData(false);
		});
		this.mainSpan = $elem.parent();
		this.menu = $(this.mainSpan.find(".combobox-menu"));
		// TODO
		if (browser.msie) {
			this.menu.bind('selectstart', function() {
				return false;
			});
		}

		this.text = $(this.mainSpan.find(":text"));
		this.hidden = $(this.mainSpan.find(":hidden"));

		this.btn = $(this.mainSpan.find(".combobox-button")[0]);
		var btn = this.btn;
		btn.on('mouseover mouseout', function(event) {
			if (event.type == 'mouseover') {
				btn.addClass("combobox-button-hover");
			} else {
				btn.removeClass("combobox-button-hover");
			}
		});

		$elem.width($elem.width() - btn.width() + 3);

		$elem.keyup(function(e) {
			self.bindTextEvent(e);
		});
		if (browser.msie) {
			this.ieBindEvent(self);
			/*
			 * $elem.bind("propertychange",function(){
			 * if(window.event.propertyName == 'value') {
			 * self.requestData(true); } });
			 */
		} else {
			$elem.bind("input", function(e) {
				self.requestData(true);
			});
		}
		// bind ref
		this.refedBy = [];
		if (this.options.refEl != '') {
			$(this.options.refEl).data("comboBox").addRef(self);
		}
		this.storePreValue();
	}
	;

	ComboBox.prototype.ieBindEvent = function(self) {
		self.text.bind("propertychange", function() {
			if (window.event.propertyName == 'value') {
				self.requestData(true);
			}
		});
	};

	ComboBox.prototype.setVal = function(self, value) {
		// if($.browser.msie) {
		if (browser.msie) {
			// IE ??
			self.text.unbind("propertychange");
			self.text.val(value);
			self.ieBindEvent(self);
		} else {
			self.text.val(value);
		}
	};

	ComboBox.prototype.addRef = function(combo) {
		this.refedBy.push(combo);
	};

	ComboBox.prototype.bindTextEvent = function(event) {
		var text = this.text;
		if (this.menu.is(":visible")) {
			return;
		}
		switch (event.keyCode) {
		case KEY.UP:
			this.requestData(false);
			break;
		case KEY.DOWN:
			this.requestData(false);
			break;
		case KEY.TAB:
			// alert('tab');
			// self.resetValue();
			// self.closeSelectDiv();
			break;
		}
	};

	ComboBox.prototype.resetRefValue = function() {
		var rarray = this.refedBy;
		if (rarray) {
			for ( var i = 0; i < rarray.length; i++) {
				// rarray[i].text.val("");
				rarray[i].setVal(rarray[i], "");
				rarray[i].hidden.val("");
				rarray[i].storePreValue();
				rarray[i].resetRefValue();
				// TODO 多级 done!
			}
		}
	};

	ComboBox.prototype.resetValue = function() {
		// TODO check the input value is in the array; if in , auto selected
		// done....?
		var value = this.text.val();

		var url = this.options.url;
		var data = this.getStaticData();
		var refEl = this.options.refEl;
		var self = this;
		var match = false;
		if (value == '') {
			this.hidden.val("");
			if (self.preValue != value) {
				self.resetRefValue();
			}
			return;
		}
		if (url != "") {
			// ajax check
			// TODO
			var p = this.options.params;
			p.time = new Date().getTime();
			p.textValue = this.text.val();
			if (refEl != '') {
				var hiddenValue = $($(refEl).parent().find(":hidden")).val();
				p.refValue = hiddenValue;
			}
			p.checkIn = true;
			$.post(url, p, function(da) {
				// alert(da);
				if (da.length == 1) {
					// self.text.val(da[0].value);
					self.setVal(self, da[0].value);

					self.hidden.val(da[0].key);
					match = true;
					if (self.preValue != value) {
						self.resetRefValue();
					}
					self.storePreValue();
				} else {
					// alert('not match');
					// self.text.val(self.preValue);
					self.setVal(self, self.preValue);
					self.hidden.val(self.preHiddenValue);
				}
			}, "json");

		} else {
			// local check
			for ( var i = 0; i < data.length; i++) {
				if (value == data[i].value + "") {
					// this.text.val(value);

					self.setVal(self, value);

					this.hidden.val(data[i].key);
					match = true;
					if (self.preValue != value) {
						this.resetRefValue();
					}
					this.storePreValue();
					break;
				}
			}
			if (!match) {
				// this.text.val(this.preValue);
				self.setVal(self, self.preValue);
				this.hidden.val(this.preHiddenValue);
			}
		}
	};

	ComboBox.prototype.storePreValue = function() {
		this.preValue = this.text.val();
		this.preHiddenValue = this.hidden.val();
	};

	ComboBox.prototype.getStaticData = function() {
		var data = this.options.dataArray;
		if (this.options.refEl != '') {
			var dd = [];
			var refValue = $(this.options.refEl).data("comboBox").hidden.val();
			if (refValue != "") {
				for ( var i = 0; i < data.length; i++) {
					var kv = data[i].key;
					if (kv == refValue) {
						dd = data[i].value;
						break;
					}
				}
			}
			data = dd;
		}
		return data;
	};

	ComboBox.prototype.requestData = function(filter) {

		var url = this.options.url;
		var data = this.getStaticData();
		var refEl = this.options.refEl;
		var self = this;
		var tvalue = this.text.val();
		if (tvalue == '') {
			this.hidden.val("");
		}
		if (url != "") {
			var p = this.options.params;
			p.time = new Date().getTime();
			if (filter) {
				p.textValue = this.text.val();
			}
			if (refEl != '') {
				var hiddenValue = $($(refEl).parent().find(":hidden")).val();
				p.refValue = hiddenValue;
			}
			p.checkIn = false;
			p.filter = filter;
			if (filter && this.text.val() == '') {
				p.filter = false;
			}
			$.post(url, p, function(da) {
				self.selectShow(da);
			}, "json");
		} else {
			if (tvalue != '') {
				if (filter) {
					data = this.filterResults(data, tvalue);
				}
			}
			this.selectShow(data);
		}
	};

	ComboBox.prototype.filterResults = function(data, tvalue) {
		var d = [];
		for ( var i = 0; i < data.length; i++) {
			if ((data[i].value + "").indexOf(tvalue) > -1) {
				d.push(data[i]);
			}
		}
		return d;
	};

	ComboBox.prototype.selectShow = function(data) {
		this.menu.empty();
		this.text.unbind("keydown");
		this.menu.css('border-width', '0');
		if (this.options.selectOnly) {
			this.menu
					.append('<div class="combobox-menuitem"><div class="combobox-menu-content combobox-menu-content-null">please select</div><div class="combobox-menu-content-hidden"></div></div>');
		}
		if (data.length == 0 && !this.options.selectOnly) {
			this.menu.append('<div class="combobox-menuitem-none">Not found.</div>');
		} else {
			for ( var i = 0; i < data.length; i++) {
				this.menu.append('<div class="combobox-menuitem"><div class="combobox-menu-content">' + data[i].value + '</div><div class="combobox-menu-content-hidden">'
						+ data[i].key + '</div></div>');
			}
		}
		this.showMenu();
	};
	ComboBox.prototype.showMenu = function() {
		var offset = this.mainSpan.position();
		// TODO offset ?
		// alert(this.mainSpan);
		// alert(this.mainSpan.innerHeight());
		var divWidth = (this.mainSpan.outerWidth() - this.menu.innerWidth()) >= 0 ? this.mainSpan.outerWidth() : this.menu.outerWidth();
		var bott = $(window).height()-this.text.position().top;
		var topMargin = offset.top + this.mainSpan.outerHeight();
		if(bott<=this.menu.height()) {
			//向上
			topMargin = offset.top - this.menu.outerHeight()-1;
			this.menu.css({
				borderTopWidth : '1px'
			});
		}else {
			this.menu.css({
				borderBottomWidth : '1px'
			});
		}
		this.menu.css({
			width : divWidth - 2,
			top : topMargin,
			left : offset.left,
			borderRightWidth : '1px',
			borderLeftWidth : '1px'
		}).show();
		var t = this.text;
		this.menu.find('.combobox-menu-content').each(function() {
			if ($(this).text() == t.val()) {
				$(this).css('font-weight', 'bold');
				$(this).parent().addClass("combobox-menu-hover");
			} else {
				$(this).css('font-weight', 'normal');
			}
		});
		var self = this;
		var menu = this.menu;
		var text = this.text;
		// this.menu.addClass("f-unselect");
		$(document).bind({
			mousedown : function(e) {
				var srcObject = e.target ? e.target : window.event.srcElement;
				do {
					if (srcObject == menu.get(0) || srcObject == text.get(0)) {
						return;
					}
					if (srcObject.tagName == 'HTML' || srcObject.tagName == 'BODY') {
						self.resetValue();
						self.closeSelectDiv();
						return;
					}
					srcObject = srcObject.parentNode;
				} while (srcObject.parentNode);
			}
		});
		$(window).bind('scroll',function(){
			self.closeSelectDiv();
		});
		this.bindMenuMouseEvent();
		this.bindMenuKeybordEvent();
	};

	ComboBox.prototype.closeSelectDiv = function() {
		//noSelect($('body'), false);
		$(document).unbind('mousedown');
		this.menu.css('border-width', '0');
		this.menu.hide();
		this.menu.empty();
		this.text.focus();
		$(window).unbind('scroll');
		var self = this;
	};

	ComboBox.prototype.bindMenuMouseEvent = function() {
		var text = this.text;
		var hidden = this.hidden;
		var menu = this.menu;
		var self = this;
		this.menu.find('.combobox-menuitem').each(function(i) {
			var item = $(this);
			var content = item.children('.combobox-menu-content')[0];
			var hiddenContent = item.children('.combobox-menu-content-hidden')[0];
			item.mouseenter(function() {
				menu.find('.combobox-menu-hover').each(function() {
					$(this).removeClass('combobox-menu-hover');
				});
				item.addClass("combobox-menu-hover");
			});
			item.mouseleave(function() {
				item.removeClass("combobox-menu-hover");
			});
			item.click(function() {
				// set value
				if (self.preValue == $(content).text()) {
					// text.val($(content).text());
					self.setVal(self, $(content).text());
					self.closeSelectDiv();
					return;
				} else {
					if ($(hiddenContent).text() == '') {
						// text.val("");
						self.setVal(self, "");
					} else {
						// text.val($(content).text());
						self.setVal(self, $(content).text());
					}
					hidden.val($(hiddenContent).text());
					self.resetRefValue();
					self.closeSelectDiv();
					self.storePreValue();
				}
			});
		});
	};

	ComboBox.prototype.bindMenuKeybordEvent = function() {
		var text = this.text;
		var hidden = this.hidden;
		var menu = this.menu;
		var self = this;
		text.keydown(function(event) {
			switch (event.keyCode) {
			case KEY.DOWN:
				event.preventDefault();
				var hover = $(menu.find('.combobox-menu-hover'));
				var hoverNext;
				if (hover.length == 0) {
					hoverNext = menu.find('.combobox-menuitem').first();
				} else {
					hoverNext = hover.next(".combobox-menuitem");
				}
				if (hoverNext.length == 0) {
					hoverNext = menu.find('.combobox-menuitem').first();
				}
				hover.removeClass('combobox-menu-hover');
				hoverNext.addClass("combobox-menu-hover");
				break;
			case KEY.RETURN:
				event.preventDefault();
				var hover = menu.find('.combobox-menu-hover');
				if (hover.length == 1) {
					var txt = $(hover.children('.combobox-menu-content')[0]).text();
					if (self.preValue == txt) {
						// text.val(txt);
						self.setVal(self, txt);
						self.closeSelectDiv();
						return;
					} else {
						if ($(hover.children('.combobox-menu-content-hidden')[0]).text() == '') {
							// text.val("");
							self.setVal(self, "");
						} else {
							// text.val(txt);
							self.setVal(self, txt);
						}
						hidden.val($(hover.children('.combobox-menu-content-hidden')[0]).text());
						self.resetRefValue();
						self.closeSelectDiv();
						self.storePreValue();
					}
				}
				break;
			case KEY.UP:
				event.preventDefault();
				var hover = $(menu.find('.combobox-menu-hover'));
				var hoverNext;
				if (hover.length == 0) {
					hoverNext = menu.find('.combobox-menuitem').last();
				} else {
					hoverNext = hover.prev(".combobox-menuitem");
				}
				if (hoverNext.length == 0) {
					hoverNext = menu.find('.combobox-menuitem').last();
				}
				hover.removeClass('combobox-menu-hover');
				hoverNext.addClass("combobox-menu-hover");
				break;
			case KEY.ESC:
				event.preventDefault();
				self.resetValue();
				self.closeSelectDiv();
				break;
			default:
				break;
			}
		});
	};
	return function(ele, opt) {
		return new ComboBox(ele, opt);
	}
});